/**
 * Copyright 2009 - ChunMap
 * 
 * 这个文件是ChunMap项目的一部分。
 * ChunMap是开源软件。
 * 你可以自由复制、传播本项目的下载包文件，但必须保持其完整性。
 * 我们不用对使用中的风险及由此造成的损失承担任何责任。
 * 详细情况请见《ChunMap许可协议》。

 * 想了解更多有关ChunMap的信息，请访问http://code.google.com/p/chunmap/
 */
package chunmap.orm.mapping;

import java.lang.reflect.Field;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import chunmap.orm.mapping.column.Column;
import chunmap.orm.mapping.column.Pk;
import chunmap.orm.mapping.relation.Link;
import chunmap.orm.mapping.type.BlobType;
import chunmap.orm.mapping.type.BooleanType;
import chunmap.orm.mapping.type.DataType;
import chunmap.orm.mapping.type.DoubleType;
import chunmap.orm.mapping.type.IntegerType;
import chunmap.orm.mapping.type.StringType;
import chunmap.util.ChunDaoException;
import chunmap.util.Function;
import chunmap.util.Reflection;

public class Table {

	private Class<?> type;
	private String name;
	private List<Column> columns = new ArrayList<Column>();
	private Pk pk;
	private List<Link> links = new ArrayList<Link>();

	public Table(Class<?> type, String name) {
		this.type = type;
		this.name = name;
	}
	
	public Table(Class<?> type) {
		this(type, type.getSimpleName());
	}

	public Column pk(Pk id) {
		addColumn(id);
		id.setTableName(name);
		this.pk = id;
		return id;
	}

	public Column addColumn(String name, DataType dataType) {
		Column column = new Column(name, dataType);
		addColumn(column);

		return column;
	}

	public void addColumn(Column column) {
		columns.add(column);
		column.setIndex(columns.size() - 1);
		column.setType(type);
	}

	public void _addLink(Link link) {
		links.add(link);
	}

	public String getName() {
		return name;
	}

	public Pk getPk() {
		return pk;
	}

	public Class<?> getType() {
		return type;
	}

	public Object readFirstObject(ResultSet rs) {
		try {
			if (rs.next()) {
				return readObject(rs);
			}
			return null;
		} catch (SQLException e) {
			throw new ChunDaoException(e);
		}
	}

	@SuppressWarnings("unchecked")
	public List readAllObject(ResultSet rs) {
		try {
			List list = new ArrayList();
			while (rs.next()) {
				Object obj = readObject(rs);
				list.add(obj);
			}
			return list;
		} catch (SQLException e) {
			throw new ChunDaoException(e);
		}
	}

	@SuppressWarnings("unchecked")
	public List readPageObject(ResultSet rs, int start, int size) {
		try {
			List list = new ArrayList();
			int count = 0;

			absolute(rs, start);
			while (count < size && rs.next()) {
				Object obj = readObject(rs);
				list.add(obj);
				count++;
			}
			return list;
		} catch (SQLException e) {
			throw new ChunDaoException(e);
		}
	}

	private void absolute(ResultSet rs, int start) throws SQLException {
		if (rs.getType() != ResultSet.TYPE_FORWARD_ONLY) {
			rs.absolute(start);
			return;
		}
		for (int i = 0; i < start; i++) {
			if (!rs.next())
				throw new IndexOutOfBoundsException();
		}
	}

	public Object readObject(ResultSet rs) {
		Object obj = Reflection.newInstance(type);
		for (Column c : columns) {
			c.readValue(obj, rs);
		}
		return obj;
	}

	public void each(Function f) {
		int i = 0;
		for (Column c : columns) {
			f.call(c, i);
			i++;
		}
	}

	@Override
	public String toString() {
		StringBuilder s = new StringBuilder();
		s.append(name + ": ");
		for (Column c : columns) {
			s.append(c.toString());
			s.append(",");
		}
		return s.toString();
	}

	// ////////////////////////////////////////////////////////////////////
	// mapping

	public static Table mapping(Class<?> type, String name) {
		Table table = new Table(type, name);

		Field[] fs = type.getDeclaredFields();
		for (Field f : fs) {

			if (f.isAnnotationPresent(Transient.class))
				continue;
			if (f.isAnnotationPresent(Col.class)) {
				Col col = f.getAnnotation(Col.class);
				if (f.isAnnotationPresent(Id.class))
					table.pk(new Pk(f.getName(), col.name(), getDataType(f)));
				else
					table.addColumn(new Column(f.getName(), col.name(),
							getDataType(f)));

				continue;
			}

			if (f.isAnnotationPresent(Id.class))
				table.pk(new Pk(f.getName(), getDataType(f)));
			else
				table.addColumn(f.getName(), getDataType(f));
		}
		
		if (table.getPk() == null)
		{
			throw new RuntimeException("not found Id");
		}
		return table;
	}

	public static DataType getDataType(Field f) {
		if (f.getType().equals(String.class)) {
			return new StringType(255);
		} else if (f.getType().equals(Boolean.class)
				|| f.getType().equals(boolean.class)) {
			return new BooleanType();
		} else if (f.getType().equals(Double.class)
				|| f.getType().equals(double.class)) {
			return new DoubleType();
		} else if (f.getType().equals(Integer.class)
				|| f.getType().equals(int.class)) {
			return new IntegerType();
		} else if (f.getType().equals(byte[].class)) {
			return new BlobType();
		}
		return new StringType(255);
	}
}
